package com.ickd.controller;

import cloud.tianai.captcha.application.ImageCaptchaApplication;
import cloud.tianai.captcha.application.vo.CaptchaResponse;
import cloud.tianai.captcha.application.vo.ImageCaptchaVO;
import cloud.tianai.captcha.common.response.ApiResponse;
import cloud.tianai.captcha.common.response.ApiResponseStatusConstant;
import cloud.tianai.captcha.common.util.CaptchaTypeClassifier;
import cloud.tianai.captcha.generator.common.model.dto.GenerateParam;
import cloud.tianai.captcha.validator.common.model.dto.ImageCaptchaTrack;
import cloud.tianai.mate.captcha.common.constant.MateCaptchaTypeConstant;
import cloud.tianai.mate.captcha.interceptor.impl.ShuffleImageProcessor;
import cloud.tianai.mate.captcha.validator.common.util.CaptchaDecryptUtils;
import com.google.gson.Gson;
import com.ickd.configuration.ResourceConfiguration;
import com.ickd.configuration.SysConfig;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;

import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;


@CrossOrigin
@RestController
@RequestMapping("/tCaptcha")
@Slf4j
public class CaptchaController {
    @Autowired
    private ImageCaptchaApplication captchaApplication;

    /**
     * 生成验证码
     *
     * @param request  request
     * @param response response
     * @param type     验证码类型
     * @param tag1     背景图片标签，如果设置则获取该标签的相关图片进行生成，如何给图片设置标签参考 {@link ResourceConfiguration}
     * @param tag2     设置模板标签，如果设置则获取该标签的相关图片进行生成，如何给图片设置标签参考 {@link ResourceConfiguration}
     * @return
     */
    @RequestMapping("/gen")
    @ResponseBody
    public CaptchaResponse<ImageCaptchaVO> genCaptcha(HttpServletRequest request,
                                                      HttpServletResponse response,
                                                      @RequestParam(value = "type", required = false) String type,
                                                      @RequestParam(value = "tag1", required = false) String tag1,
                                                      @RequestParam(value = "tag2", required = false) String tag2
    ) {

//        String type = sysConfig.getCaptchaType();
        if (StringUtils.isEmpty(type)) {
            type = "SLIDER2,CURVE";
        }
        if (type.contains(",")) {
            String[] types = type.split(",");
            type = types[(new Random()).nextInt(types.length)];
        }
        if (StringUtils.isEmpty(type)) {
            type = MateCaptchaTypeConstant.SLIDER2;
        }
        type = type.toUpperCase();
        GenerateParam param = new GenerateParam();
        param.setType(type);
        param.setBackgroundImageTag(tag1);
        param.setTemplateImageTag(tag2);
        // 扩展功能， 是否将生成图片随机打乱(打乱后会在这前端用canvas重组)
        param.addParam(ShuffleImageProcessor.SHUFFLE_IMAGE_KEY, true);
        // 这行代码设置为true时表示开启混淆
        param.setObfuscate(false);
        if (param.getObfuscate()) {
            if ("ROTATE".equals(type)) {
                // 如果是旋转验证码，开启混淆后，选用有混淆功能的模板
                param.setTemplateImageTag("obfuscate");
            }
            if (CaptchaTypeClassifier.isClickCaptcha(type)) {
                // 开启混淆后，这个用来调整点选类图片的扭曲程度，数值越大扭曲越厉害
                param.addParam("clickImageDistort", ThreadLocalRandom.current().nextInt(3, 7));
            }
        }
        return captchaApplication.generateCaptcha(param);
    }


    /**
     * 验证函数 （两阶段验证）
     *
     * @param validateDto 加密数据
     * @param request     request
     * @return ApiResponse<?>
     */
    @PostMapping("/check")
    @ResponseBody
    public ApiResponse<?> checkCaptcha(@RequestBody ValidateDto validateDto, HttpServletRequest request) {
        System.out.println("执行验证程序");
        ImageCaptchaTrack sliderCaptchaTrack;
        EncData captchaData = validateDto.getCaptcha();
        ParamsBody paramsBody = validateDto.getBody();
        if (captchaData == null || paramsBody == null) {
            return ApiResponse.ofMessage(ApiResponseStatusConstant.NOT_VALID_PARAM);
        }
        try {
            sliderCaptchaTrack = decode(validateDto.getCaptcha(), request);
        } catch (Exception e) {
            e.printStackTrace();
            // 解密失败
            return ApiResponse.ofMessage(ApiResponseStatusConstant.NOT_VALID_PARAM);
        }
        // 调用 matching 进行校验
        ApiResponse<?> response = captchaApplication.matching(captchaData.getId(), sliderCaptchaTrack);
        log.debug("校验结果\n{}", new Gson().toJson(response));
        if (response.isSuccess()) {
            ApiResponse<Object> convert = response.convert();
            // 验证成功后返回验证token，默认captcha-mate 内部实现了简单的二次验证逻辑， 配置文件中设置captcha.secondary.enabled=true即可
            // 如果想自己实现二次验证的逻辑，这里返回自己生成的验证token，并将captcha.secondary.enabled=false 设置为false，关闭内置的二次验证逻辑
//            convert.setData(Collections.singletonMap("token", captchaData.getId()));
            String mailNo = paramsBody.getMailNo();
            String com = paramsBody.getCom();
            if (StringUtils.isNoneEmpty(mailNo, com)) {
                convert.setData(Collections.singletonMap("token", getToken(com, mailNo)));
            } else {
                convert.setData(Collections.singletonMap("token", getCommonToken(request)));
            }
            return convert;
        }
        return response;
    }

    private Object getCommonToken(HttpServletRequest request) {
        String referer = request.getHeader("referer");
        long timestamp = (System.currentTimeMillis() / 1000);
        String str = referer + timestamp + "&$XX@)#FSSK3U";
        return DigestUtils.md5DigestAsHex(str.getBytes(StandardCharsets.UTF_8)) + "-" + timestamp;
    }

    private String getToken(String com, String mailNo) {
        long timestamp = (System.currentTimeMillis() / 1000);
        String str = com + timestamp + mailNo + "&$@)#FKU";
        return DigestUtils.md5DigestAsHex(str.getBytes(StandardCharsets.UTF_8)) + "-" + timestamp;
    }

    /**
     * 模拟登录业务
     *
     * @param body
     * @return
     */
//    @PostMapping("/login")
//    private ApiResponse<?> login(@RequestBody OtherBody body) {
////        String token = body.getToken();
//        if (StringUtils.isBlank(token)) {
//            return ApiResponse.ofError("验证码失效");
//        }
//        // 二次验证验证码token 只有开启了 captcha.secondary.enabled=true 才可转换为 SecondaryVerificationApplication
//        if (!((SecondaryVerificationApplication) captchaApplication).secondaryVerification(token)) {
//            return ApiResponse.ofError("验证码失效");
//        }
//
////        String username = body.getUsername();
////        String password = body.getPassword();
//        // 具体业务逻辑
//        // ...
//
//        // 验证成功,这里返回自己的业务对象即可
//        return ApiResponse.SUCCESS;
//    }

//    /**
//     * 单阶段验证， 验证验证码 和处理自定义逻辑都在 一次请求处理
//     *
//     * @param body
//     * @param request
//     * @return
//     */
////    @PostMapping("/onestage")
//    private ApiResponse onestage(@RequestBody ValidateDto body, HttpServletRequest request) {
//        ApiResponse<?> response = checkCaptcha(body, request);
//        if (!response.isSuccess()) {
//            // 验证成功
//            return response;
//        }
//        // 处理自定义逻辑
//        ApiResponse<Object> convert = response.convert();
//        convert.setMsg("密码错误");
//        return convert;
//    }


    /**
     * 一个解密函数，直接拷贝到自己项目里即可
     *
     * @param data    加密的数据
     * @param request 请求参数
     * @return ImageCaptchaTrack
     */
    private ImageCaptchaTrack decode(EncData data, HttpServletRequest request) {
        return CaptchaDecryptUtils.decrypt(data.getData());
    }

    @lombok.Data
    public static class EncData {
        private String id;
        private String data;
    }

    @Data
    public static class ValidateDto {
        private ParamsBody body;
        private EncData captcha;
    }

    @Data
    public static class ParamsBody {
        private String mailNo;
        private String com;

    }
}
